#include "CBL_PoolManager.h"
#include <algorithm>

CB_SUBNAMESPACE_START(Memory)

static const size_t FreeListBlockCount	= 100;
static const size_t FreeListListCount	= 10;

// -----------------------------------------------------------------------------

CPoolManager::CPoolManager()
{
}

// -----------------------------------------------------------------------------

struct DeleteOp
{
	void operator () (CFreeList* _pFreeList)
	{
		delete _pFreeList;
	}
};

CPoolManager::~CPoolManager()
{
	for (size_t i(0); i<SizeBucketGranularity; ++i)
	{
		CBucketVector& rCurrentBucket = m_SizeBuckets[i];
		std::for_each(rCurrentBucket.begin(), rCurrentBucket.end(), DeleteOp());
	}
}

// -----------------------------------------------------------------------------

CFreeList* CPoolManager::GetSlot(uint32 _Index, uint32 _BlockSize)
{
	CBucketVector& rBucket = m_SizeBuckets[_Index];
	
	CBucketVector::iterator Iter = rBucket.begin();
	const CBucketVector::iterator EndIter = rBucket.end();
	
	while (Iter != EndIter)
	{
		if ((*Iter)->GetBlockSize() == _BlockSize)
			return *Iter; // ### return ###
		++Iter;
	}
	
	return NULL; // ### return ###
}

// -----------------------------------------------------------------------------

void CPoolManager::AddFreeList(CFreeList* _pList)
{
	m_AllocatorLock.WriteLock();
	
	uint32 BlockSize = _pList->GetBlockSize();
	uint32 Index = IndexForSize(BlockSize);
	
	if (Debug::Verify(GetSlot(Index, BlockSize) == NULL, 
					  "A CFreeList of that size has already been registered to CPoolManager."))
	{
		CBucketVector& rBucket = m_SizeBuckets[Index];
		rBucket.push_back(_pList);
	}
	
	m_AllocatorLock.Unlock();
}

// -----------------------------------------------------------------------------

CFreeList* CPoolManager::FindListForSize(uint32 _BlockSize)
{
	m_AllocatorLock.ReadLock();
	
	uint32 Index = IndexForSize(_BlockSize);
	CFreeList* pFreeList = GetSlot(Index, _BlockSize);
	
	if (pFreeList == NULL)
	{
		m_AllocatorLock.UpgradeLock();
		
		CBucketVector& rBucket = m_SizeBuckets[Index];
	
		pFreeList = new CFreeList(_BlockSize, FreeListBlockCount, FreeListListCount, false);
		rBucket.push_back(pFreeList);
	}
	
	m_AllocatorLock.Unlock();
	
	return pFreeList; // ### return ###
}

CB_SUBNAMESPACE_END